//go:build wireinject

package ioc

import (
	"context"
	"fmt"
	"gitee.com/flycash/simple-im/internal/domain"
	"github.com/gotomicro/ego/core/econf"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
	"time"

	imgrpc "gitee.com/flycash/simple-im/internal/api/grpc"
	"gitee.com/flycash/simple-im/internal/event/msg"
	"gitee.com/flycash/simple-im/internal/ioc"
	"gitee.com/flycash/simple-im/internal/repository"
	"gitee.com/flycash/simple-im/internal/repository/dao"
	"gitee.com/flycash/simple-im/internal/service"
	"gitee.com/flycash/ws-gateway/pkg/jwt"
	"github.com/ecodeclub/mq-api"
	"github.com/ego-component/egorm"
	"github.com/google/wire"
	"github.com/gotomicro/ego/server/egrpc"
)

var (
	baseSet = wire.NewSet(
		ioc.InitDB,
		ioc.InitMQ,
		ioc.InitEtcdClient,
		ioc.InitRegistry,
	)
	msgSvcSet = wire.NewSet(
		dao.NewGORMMessageDAO,
		dao.NewGORMChannelMemberDAO,
		dao.NewGORMChannelDAO,
		repository.NewMessageRepository,
		repository.NewChannelRepository,
		initProducer,
		initTokenGenerator,
		initMsgSvc,
	)
)

type App struct {
	GrpcServers []*egrpc.Component
}

func initMsgSvc(db *gorm.DB, tokenGenerator *jwt.UserToken, chanRepo repository.ChannelRepository, repo repository.MessageRepository, pro msg.Producer) service.MessageService {
	svc := service.NewMessageService(repo, chanRepo, pro)
	initUsers(db, tokenGenerator)
	initChannel1(db)
	return svc
}

func initProducer(testMq mq.MQ) msg.Producer {
	topic := econf.GetString("pushMessageEvent.topic")
	pro, err := testMq.Producer(topic)
	if err != nil {
		panic(err)
	}
	return msg.NewProducer(pro, topic)
}

func initTokenGenerator() *jwt.UserToken {
	return jwt.NewUserToken(jwt.UserJWTKey, "simple-im")
}

func initUsers(db *egorm.Component, tokenGenerator *jwt.UserToken) {
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()
	for i := 1; i < 20; i++ {
		token, err := tokenGenerator.Encode(jwt.UserClaims{
			BizID:  1,
			UserID: int64(i),
		})
		if err != nil {
			panic(err)
		}
		err = db.WithContext(ctx).
			Clauses(clause.OnConflict{
				DoNothing: true,
			}).
			Create(&dao.User{
				ID:       int64(i),
				Nickname: fmt.Sprintf("user_%d", i),
				Avatar:   "333.com",
				Token:    token,
			}).Error
		if err != nil {
			panic(err)
		}
	}

}

func initChannel1(db *egorm.Component) {
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()
	now := time.Now().UnixMilli()
	err := db.WithContext(ctx).
		Clauses(clause.OnConflict{
			DoNothing: true,
		}).
		Create(&dao.Channel{
		ID:    1,
		Name:  "channel_1",
		Type:  domain.OneToOneChannel,
		Ctime: now,
		Utime: now,
	}).Error
	if err != nil {
		panic(err)
	}
	err = db.WithContext(ctx).
		Clauses(clause.OnConflict{
			DoNothing: true,
		}).
		Create([]dao.ChannelMember{
		{
			ChannelID: 1,
			UID:       int64(1),
			JoinTime:  now,
		},
		{
			ChannelID: 1,
			UID:       int64(2),
			JoinTime:  now,
		},
	}).Error
	if err != nil {
		panic(err)
	}
}

func InitApp() *ioc.App {
	wire.Build(
		baseSet,
		msgSvcSet,

		imgrpc.NewMsgServer,
		ioc.InitGrpc,
		wire.Struct(new(ioc.App), "*"),
	)
	return new(ioc.App)
}
